<?php


namespace App\Utils;


use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;

class HttpUtil
{
    private $client;

    const MAX_RETRIES = 5;
    const TIMEOUT = 60;
    const CONNECT_TIMEOUT = 30;

    public function getClient()
    {
        if (is_null($this->client)) {
            //添加失败重试中间件
            $handlerStack = HandlerStack::create(new CurlHandler());
            $handlerStack->push(Middleware::retry(self::retryDecider(), self::retryDelay()));
            $result = $this->proxy();
            $proxy = isset($result['proxy']) ? ['proxy'=> $result['proxy']] : [];
            $this->client = new Client(array_merge(
                [
                    'handler' => $handlerStack,
                    'timeout' => self::TIMEOUT,
                    'connect_timeout' => self::CONNECT_TIMEOUT,
                ]
                ,$proxy ));
        }
        return $this->client;
    }

    /**
     * retry decider
     * 重试决策
     * @return \Closure
     */
    protected static function retryDecider()
    {
        return function (
            $retries,
            Request $request,
            Response $response = null,
            RequestException $exception = null
        ) {
            if ($retries >= self::MAX_RETRIES) {
                return false;
            }

            if ($exception instanceof ConnectException) {
                return true;
            }

            if ($response) {
                if ($response->getStatusCode() >= 429) {
                    return true;
                }
            }

            return false;
        };
    }

    /**
     * retry delay
     * 重试延迟时间
     * @return \Closure
     */
    protected static function retryDelay()
    {
        return function ($numberOfRetries) {
            return 1000 * $numberOfRetries;
        };
    }

    public function request($method, $uri = '', array $options = [])
    {
        $response = $this->getClient()->get('http://127.0.0.1:5010/get/');
        $ip = $response->getBody()->getContents();
        Logger::getLogger("proxy")->info($ip);
        if ($ip) {
            $options['proxy'] = $ip;
        }
        return $this->getClient()->request($method, $uri, $options);
    }

    public function proxy()
    {
        try {
            $response = (new Client())->get('http://172.23.0.1:5010/get/');
            $ip = $response->getBody()->getContents();
            Logger::getLogger("proxy")->info($ip);
            return $ip ?? [];
        } catch (\Exception $e) {
            return [];
        }
    }
}